#include "Sprite.h"
#include <time.h>
#include <stdlib.h>

/** Reads a xpm-like sprite defined in "map" (look at pixmap.h for
 * examples). Returns the address of the allocated memory where the
 * sprite was read. Updates "width" and "heigh" with the sprite
 * dimension.  Return NULL on error.
 * Assumes that VRES and HRES (the screen vertical and horizontal resolution)
 * are externaly defined.
 * 
 * Usage example, using the defined sprite in pixmap.h:
 * <pre>
 *   #include "pixmap.h" // defines  pic1, pic2, etc 
 *   int wd, hg;
 *   char *sprite = read_xpm(pic1, &wd, &hg);
 * </pre>
*/
char *read_xpm(char *map[], int *wd, int *ht)
{
	__attribute__((unused)) static char read_xpm_jcard;
	int width, height, colors;
	char sym[256];
	int  col;
	int i, j;
	char *pix, *pixtmp, *tmp, *line;
	char symbol;

	// read width, height, colors
	if (sscanf(map[0],"%d %d %d", &width, &height, &colors) != 3) {
		printf("read_xpm: incorrect width, height, colors\n");
		return NULL;
	}
#ifdef DEBUG
	printf("%d %d %d\n", width, height, colors);
#endif
	if (width > HRES || height > VRES || colors > 256) {
		printf("read_xpm: incorrect width, height, colors\n");
		return NULL;
	}

	*wd = width;
	*ht = height;

	for (i=0; i<256; i++)
	sym[i] = 0;

	// read symbols <-> colors 
	for (i=0; i<colors; i++) {
		if (sscanf(map[i+1], "%c %d", &symbol, &col) != 2) {
			printf("read_xpm: incorrect symbol, color at line %d\n", i+1);
			return NULL;
		}
#ifdef DEBUG
		printf("%c %d\n", symbol, col);
#endif
		if (col > 256) {
			printf("read_xpm: incorrect color at line %d\n", i+1);
			return NULL;
		}
		sym[col] = symbol;
	}

	// allocate pixmap memory
	pix = pixtmp = malloc(width*height);

	// parse each pixmap symbol line
	for (i=0; i<height; i++) {
		line = map[colors+1+i];
#ifdef DEBUG
		printf("\nparsing %s\n", line);
#endif
		for (j=0; j<width; j++) {
			tmp = memchr(sym, line[j], 256);  // find color of each symbol
			if (tmp == NULL) {
				printf("read_xpm: incorrect symbol at line %d, col %d\n", colors+i+1, j);
				return NULL;
			}
			*pixtmp++ = tmp - sym; // pointer arithmetic!
#ifdef DEBUG
			printf("%c:%d ", line[j], tmp-sym);
#endif
		}
	}

	return pix;
}

/** Creates with random speeds (not zero) and position
 * (within the screen limits), a new sprite with pixmap "pic", in
 * memory whose address is "base".
 * Returns NULL on invalid pixmap.
 */
Sprite * create_sprite(char *pic[], char *base, int posx, int posy)
{
	/*creates a new sprite pointer*/
	Sprite *sp;
	sp= (Sprite*) malloc(sizeof(Sprite));
	/*inicializes position values*/
	sp->x=posx;
	sp->y=posy;
	/*inicializes random speeds*/
	srand(time(NULL));
	sp->xspeed = gen_random(-2, 2);
	sp->yspeed = gen_random(-2, 2);
	
	while(sp->xspeed == 0 || sp->yspeed == 0){
		sp->xspeed = gen_random(-2, 2);
		sp->yspeed = gen_random(-2, 2);
	}
	/*  width and height parameters*/
	int wd, hg;
	sp->map = read_xpm(pic, &wd, &hg); 
	/*updates width and height*/
	sp->width = wd;
	sp->height = hg;
	return sp;
}

void show_sprite(Sprite* fig, char* base) {
	
	int hg = fig->height;
	int wd = fig->width;
	char * mapinit=fig->map;
	int i,j;
	
	//if xspeed is greater than 0
	if (fig->xspeed>0)
	{
		for(j=fig->y;j<hg+fig->y; j++)
		for(i=fig->x; i<wd+fig->x; i++)
		
		set_pixel(i,j,*(fig->map++),base);
		
	}
	else
	{
		//if xspeed is lower than 0
		if (fig->xspeed<0)
		{
			for(j=fig->y;j<hg+fig->y; j++)
			for(i=wd+fig->x-1; i>=fig->x; i--)
			
			set_pixel(i,j,*(fig->map++),base);
			
		}
		//if xspeed is 0
		else
		{
			//if yspeed is greater than 0
			if (fig->yspeed>0)
			{

				int x=0;
				int y=0;
				for(j=fig->y;j<hg+fig->y; j++)
				{
					
					for(i=fig->x; i<wd+fig->x; i++)
					{		
						set_pixel(i,j,*(fig->map+49*y+x),base);
						y++;
					}
					x++;
					y=0;
				}
				
			}
			
			else
			{
				//if yspeed is lower than 0
				if (fig->yspeed<0)
				{
					
					
					int x=0;
					int y=0;
					for(j=hg+fig->y-1;j>=fig->y; j--)
					{
						for(i=fig->x; i<wd+fig->x; i++)
						{		
							set_pixel(i,j,*(fig->map+49*y+x),base);
							y++;
						}
						x++;
						y=0;
					}
				}
				//if yspeed is  0
				else
				{
					for(j=fig->y;j<hg+fig->y; j++)
					for(i=fig->x; i<wd+fig->x; i++)
					
					set_pixel(i,j,*(fig->map++),base);
				}
			}
		}
	}
	fig->map=mapinit;
	
}

/** Animate the sprite "fig" according to its attributes in memory, 
 * whose address is "base".
 * The animation detects the screen borders
 * and change the speed according; it also detects collision with
 * other objects, including the player pad. Collisions with the screen
 * border generates a perfect reflection, while collision with other
 * objects generates a random perturbation in the object speed. Other
 * strategies can be devised: use quasi-elastic collision based on the
 * objects "mass" and speed, generate spin effect based on amount of
 * tangential speed direction relative to the object center of
 * "mass"...  Returns the kind of collision detected, RIGHT_HIT or
 * LEFT_HIT, if a collision with the players pad (WHITE colored!) is
 * detected.
 */
 
int animate_sprite(Sprite *fig, char *base)
{
	int col = 0;
	int coordXColideFig=-1;
	int coordYColideFig=-1;
	
	Note nota1;
	nota1.freq = Fa2;
	nota1.dur = 10;
	
	erase_sprite(fig,0, base);
	col = colides(fig, fig->x + fig->xspeed, fig->y + fig->yspeed, base, &coordXColideFig, &coordYColideFig);
	
	switch (col)	{
	case COLIDE_TOP :
		fig->yspeed = -fig->yspeed;
		break;
	case COLIDE_BOTTOM :
		fig->yspeed = -fig->yspeed;
		break;
	case COLIDE_LSIDE :
		fig->xspeed = -fig->xspeed;
		break;
	case COLIDE_RSIDE :
		fig->xspeed = -fig->xspeed;
		break;
	case COLIDE_FIG :
	
		if (coordXColideFig!=-1 && coordYColideFig!=-1)
		{
			fig->xspeed = -fig->xspeed;
			if(fig->x > 596 || fig->x < 29)
				fig->yspeed = -fig->yspeed;
		}
		break;
		// case there is no colision
	case NO_COL :
		break;
	}
	//refresh current cordinates and draws sprite
	fig->x = fig->x + fig->xspeed;
	fig->y = fig->y + fig->yspeed;
	show_sprite(fig, base);
	
	return 0;
}


/** The "fig" sprite is erased from memory whose address is "base"
 * and used resources released.
 */
 
 void erase_sprite(Sprite* fig, int back, char* base) {
	int i,j;
	for(j=fig->y; j<fig->height+fig->y; j++) {			
		for(i=fig->x; i<fig->width+fig->x; i++) {
			set_pixel(i,j,back,base);
		}
	}
}
 
void destroy_sprite(Sprite *fig, char *base)
{
	/*indexes of sprite*/
	erase_sprite(fig, 0, base);

	/* frees memory */
	free(fig);
	return;
}

int colides(Sprite *fig, int newX, int newY, char *base, int * coordXColideFig, int * coordYColideFig)	{
	int j, color;
	if(fig->xspeed > 0) {
		if((newX + fig->width) >= 633) {
			//colides with a border
			return COLIDE_RSIDE;
		}
		for(j = 0; j < (fig->height); j++){
			color = get_pixel((newX+fig->width-1), (newY+j), base);
			if(color != BLACK)	{	
				if (color != GREEN){
					(*coordXColideFig)=(newX+fig->width-1);
					(*coordYColideFig)=(newY+j);
				}
				//colides with another sprite
				return COLIDE_FIG;
			}
		}
	}
	if(fig->xspeed < 0) {
		if((newX) <= 6 ) {
			//colides with a border
			return COLIDE_LSIDE;
		}
		for(j = 0; j < (fig->height); j++){
			color = get_pixel((newX), (newY+j), base);
			if(color != BLACK)	{			

				if (color != GREEN){
					(*coordXColideFig)=(newX);
					(*coordYColideFig)=(newY+j);
				}
				//colides with another sprite
				return COLIDE_FIG;	
			}
		}
	}
	if(fig->yspeed > 0) {
		if((newY+fig->height) >= 473) {
			//colides with a border
			return COLIDE_BOTTOM;
		}
		
		for(j = 0; j < (fig->width); j++){
			color = get_pixel((newX+j), (newY+fig->height-1), base);
			if(color != BLACK)	{
				if (color != GREEN){
					(*coordXColideFig)=(newX+j);
					(*coordYColideFig)=(newY+fig->height-1);
				}
				//colides with another sprite
				return COLIDE_FIG;
			}
		}
	}
	if(fig->yspeed < 0) {
		if((newY) <= 107) {
			//colides with a border
			return COLIDE_TOP;
		}
		for(j = 0; j < (fig->width); j++){
			color = get_pixel((newX+j), (newY), base);
			if(color != BLACK)	{					
				if (color != GREEN){
					(*coordXColideFig)=(newX+j);
					(*coordYColideFig)=(newY);
				}
				//colides with another sprite
				return COLIDE_FIG;	
			}
		}
		
	}
	//colides with another sprite
	return NO_COL;
}

/** Move in memory whose address is 'base', the 'fig' cursor, a
 * standard sprite, from its current position to a new position
 * 'xstep' and 'ystep' away.  
 * The cursor is erased from its present position in xor-mode and draw
 * in the new position also oin xor-mode.
 * The first time this function is called it only draws the cursor at the
 * current position.
 */ 
void move_cursor(Sprite *fig, int xstep, int ystep, char *base);


int gen_random(int x, int y) 
{
	//gera um numero entre x e y
	int r_number = (rand() % y) + x;
	
	return r_number;
}



/** @} end of sprite */

